#include <SDL.h>
#include <stdlib.h>
extern "C" {
#include <avcodec.h>
#include <avformat.h>
}

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include "../../SDL_primitives/SDL_Line.h"
#include "../../BlobDetection/C++/BlobDetection.h"
#include "../../BlobDetection/C++/Blob.h"
#include "../../PortVideo/common/SFont.h"
#include "../portVideoBlobDetection/Tools.h"

#define FALSE 0
//============
SDL_Surface *screen;
Uint16 *raw_pixels;
//=========

int image_width;
int image_height;



//swap the pixels before displying it as drivers will return in BGR format
void swap_rgb24(unsigned char *mem, int n)
{
	unsigned char  c;
	unsigned char *p = mem;
	unsigned int   i = n ; 

	while (--i) 
	{
		c = p[0];

		p[0] = p[2];
		p[2] = c;

		p += 3;
	}
}         


void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame, blobDetection::BlobDetection &theBlobDetection,int *imageData, SFont_Font *sfont, bool drawFont)
{
	
	blobDetection::Blob b;
	b.setParent(&theBlobDetection);
	blobDetection::EdgeVertex eA,eB;
	
	Uint32 green = SDL_MapRGB(screen->format, 0x00, 0xff, 0x00);
	Uint32 red = SDL_MapRGB(screen->format, 0xff, 0x00, 0x00);
	
	unsigned char *buffer;	
	int  y;
	uint8_t *pixel=NULL;

	//SDL_LockSurface(screen);

	pixel=(uint8_t*)screen->pixels;
	for(y=0;y<height;y++)
	{
		memcpy(pixel+y*pFrame->linesize[0], pFrame->data[0]+y*pFrame->linesize[0], width*3);
	}

	int x,c;
	
	uint8_t *data = pixel;

	for( y = 0; y < height; y++, data += pFrame->linesize[0] )
		for( x = 0; x < width*3; x+=3 )
		{
			unsigned char r = data[x];
			unsigned char g = data[x+1];
			unsigned char b = data[x+2];
			
			int rint = r;
			rint = (rint & 0x000000FF);
			
			int gint = g;
			gint = (gint & 0x000000FF) << 8;
		
			int bint = b;
			bint = (bint & 0x000000FF) << 16;
		
			imageData[x/3 +y*width] = rint | gint | bint;
		
			//printf("RGB: %d %d %d %d\n", r, g, b, imageData[x +y*img->height]);
		
		}
	
	theBlobDetection.computeBlobs(imageData);
	//printf("Blobs Count: %d\n",theBlobDetection.getBlobNb());
	for (int n=0 ; n<theBlobDetection.getBlobNb() ; n++)
	{
		b = theBlobDetection.getBlob(n);
		if (b.isOk())
		{

			for (int m=0;m<b.getEdgeNb();m++)
				{
					eA = b.getEdgeVertexA(m);
					eB = b.getEdgeVertexB(m);
					if (eA.isOk() && eB.isOk())
						 SDL_Line( screen, eA.x*width, eA.y*height, eB.x*width, eB.y*height, red );
				}

			SDL_Rectangle( screen, b.xMin*width , b.yMin*height, b.xMax*width, b.yMax*height, green);
			std::string blobId =  toString<int>(b.id);
			
			if( drawFont) SFont_Write(screen, sfont, b.x*width,b.y*height,blobId.c_str());

		}

      }

	//buffer = (unsigned char *)pixel;
	//swap_rgb24(buffer, height*width);
	//SDL_Rectangle(screen,50,50,100,100,green);
	//SDL_Rectangle(screen,150,150,200,200,green);
				
	//SDL_UnlockSurface(screen);

	SDL_UpdateRect(screen,0,0,0,0);//x,y,w,h are all 0 so it will update entire screen.

}
//signal handler function
void signal_handler (int signum)
{
	printf("\nSignal handler called to interupt the program.\n");
	exit(1);
}

int main(int argc, char *argv[])
{
	AVFormatContext *pFormatCtx;
	int             i, videoStream;
	AVCodecContext  *pCodecCtx;
	AVCodec         *pCodec;
	AVFrame         *pFrame; 
	AVFrame         *pFrameRGB;
	AVPacket        packet;
	int             frameFinished;
	int             numBytes;
	uint8_t         *buffer;

	//Handling CTRL-C signal.
	signal(SIGINT, signal_handler);

	//*******************SDL code***********************
	if (SDL_Init(SDL_INIT_VIDEO) != 0) {
		printf("Unable to initialize SDL: %s\n",
				SDL_GetError());
		return 1;
	};
	atexit(SDL_Quit);
	//*************************************

	// Register all formats and codecs
	av_register_all();

	// Open video file
	if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
		return -1; // Couldn't open file

	// Retrieve stream information
	if(av_find_stream_info(pFormatCtx)<0)
		return -1; // Couldn't find stream information

	// Dump information about file onto standard error
	dump_format(pFormatCtx, 0, argv[1], FALSE);

	// Find the first video stream
	videoStream=-1;
	for(i=0; i<pFormatCtx->nb_streams; i++)
		//        if(pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO)
		if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
		{
			videoStream=i;
			break;
		}
	if(videoStream==-1)
		return -1; // Didn't find a video stream

	// Get a pointer to the codec context for the video stream
	// pCodecCtx=&pFormatCtx->streams[videoStream]->codec;
	pCodecCtx=pFormatCtx->streams[videoStream]->codec;

	// Find the decoder for the video stream
	pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
	if(pCodec==NULL)
		return -1; // Codec not found

	// Open codec
	if(avcodec_open(pCodecCtx, pCodec)<0)
		return -1; // Could not open codec


	//****************************SDL API --------------	
	screen = SDL_SetVideoMode(pCodecCtx->width,pCodecCtx->height,24,SDL_SWSURFACE);
	//***************************************************
	SFont_Font *sfont;
	int font_height;
	
	sfont = SFont_InitDefaultFont();
	font_height = SFont_TextHeight(sfont);
	bool drawFont = true;

	// Hack to correct wrong frame rates that seem to be generated by some codecs
	//if(pCodecCtx->time_base.den>1000 && pCodecCtx->time_base.num==1)
	//	pCodecCtx->time_base.num=1000;

	// Allocate video frame
	pFrame=avcodec_alloc_frame();

	// Allocate an AVFrame structure
	pFrameRGB=avcodec_alloc_frame();
	if(pFrameRGB==NULL)
		return -1;

	// Determine required buffer size and allocate buffer
	numBytes=avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width,
			pCodecCtx->height);
	buffer= (uint8_t*)malloc(numBytes * sizeof(uint8_t));

	// Assign appropriate parts of buffer to image planes in pFrameRGB
	avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_BGR24,
			pCodecCtx->width, pCodecCtx->height);

	image_width = pCodecCtx->width;
	image_height = pCodecCtx->height;

	int frameCount = 0;
	float speed = 1.0;
	float fps = av_q2d(pCodecCtx->time_base);
	// Read frames and save frames to disk
	i=0;
	
	int running = 1;
	int paused = 0;
	SDL_Event event;
	int err;
	
	int *imageData = new int[pCodecCtx->width * pCodecCtx->height];

	blobDetection::BlobDetection theBlobDetection(pCodecCtx->width, pCodecCtx->height,3);
	theBlobDetection.setPosDiscrimination(false);
	theBlobDetection.setThreshold(0.37f);
	theBlobDetection.setBlobDimensionMin(3, 3);
	
		while (running) {
		// interactivity ...
		while (SDL_PollEvent(&event)) {
			switch(event.type) {
				case SDL_KEYDOWN:
					switch(event.key.keysym.sym) {
						case SDLK_LEFT:
							// half of current speed: 1 -> 0.5 -> 0.25 -> 0.125 ...
							speed *= 2.0;
							break;
						case SDLK_RIGHT:
							// double the current speed:
							speed *= 0.5;
							break;
						case SDLK_UP:
							// reset the speed
							speed = 1.0;
							break;
						case SDLK_f:
							// full screen:
							SDL_WM_ToggleFullScreen(screen);
							break;
						case SDLK_l:
							// font on/off
							drawFont = !drawFont;
							break;
						case SDLK_SPACE:
							// pause and resume ...
							paused = !paused;
							break;
						case SDLK_ESCAPE:
							// clean shutdown with flag 'running'
							running = 0;
							break;
						default:
							break;
					}
				default:
					break;
			}
		}
		// while the thing was not paused read frames ....
		// if it is paused do not read & decode, only show the current frame on and on. 
		// so the video decompression and stream is paused but not the interface.
		if (!paused) {
		    // read the next (encoded) frame data into pkt.
			err = av_read_frame(pFormatCtx, &packet);
			if (err < 0) {
				if( err == -1)
				{
					return 0;	
				}
				fprintf(stderr, "Could not read packet: error %d\n", err);
				return 1;
			}
		}
	
		// Is this a packet from the video stream?
		if(packet.stream_index==videoStream)
		{
			// Decode video frame
			if (!paused) {
				avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
					packet.data, packet.size);
			}
			// Did we get a video frame?
			if(frameFinished)
			{
				frameCount++;
				if (frameCount%5==0) {
					char caption[12];
					sprintf(caption, "%4.1f s, speed %2.3fx (%2.1f fps)", frameCount*fps, 1/speed, 1/fps);
					SDL_WM_SetCaption(caption, caption);
				}
				
				// Convert the image from its native format to RGB
				img_convert((AVPicture *)pFrameRGB, PIX_FMT_BGR24, 
						(AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, 
						pCodecCtx->height);
				// Save the frame to disk as well as display it to SDL surface.
				SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,i++,theBlobDetection,imageData,sfont,drawFont);
				//SDL_Delay(50);
				SDL_Delay(1000*fps*speed);	
			}

		}

		// Free the packet that was allocated by av_read_frame
		av_free_packet(&packet);
	}

	// Free the RGB image
	free(buffer);
	av_free(pFrameRGB);

	// Free the YUV frame
	av_free(pFrame);

	// Close the codec
	avcodec_close(pCodecCtx);

	// Close the video file
	av_close_input_file(pFormatCtx);
	//=================
	//SDL code
	SFont_FreeFont(sfont);
	
	SDL_Quit();
	//==============
	return 0;
}

